
#include "AutoFind.h"

std::mutex log_lock;
///日志锁
static void lock_function(bool lock, void *udata) {
    if (lock) {
        // 获取锁
        log_lock.lock();
    } else {
        // 释放锁
        log_lock.unlock();
    }
}

static int create_directory(const char *path) {
    // 设置文件夹权限为 rwxr-xr-x
    mode_t mode = 0755;

    // 创建文件夹
    if (mkdir(path, mode) == -1) {
        // 如果文件夹已经存在，返回0
        if (errno == EEXIST) {
            return 0;
        }
        // 其他错误，返回-1
        perror("mkdir failed");
        return -1;
    }

    return 0;
}

AutoFind::AutoFind() : socket_(INVALID_SOCKET){
#if defined(_WIN32) || defined(_WIN64)
    WSADATA wsaData;
    init_ok = !WSAStartup(MAKEWORD(2, 2), &wsaData);
#elif defined(__linux__)
    init_ok = true;
#else
#error "Unsupported operating system"
#endif
    create_directory("logs");
    file = fopen("logs/auto-server.log", "a");
    if (file != NULL)
    {
        log_add_fp(file, LOG_INFO); // 只输出信息级别以上的日志到文件
    }
    log_set_lock(lock_function, NULL);
}

AutoFind::~AutoFind() {
#if defined(_WIN32) || defined(_WIN64)
    if(init_ok){
        WSACleanup();
    }
#elif defined(__linux__)
    init_ok = false;
#else
#error "Unsupported operating system"
#endif
    if(socket_!=INVALID_SOCKET) {
        closesocket(socket_);
    }
    if (file != NULL)
    {
        fclose(file);
        file = NULL;
    }
}

void AutoFind::setHost(char *host) {
    this->host = std::string(host);
}

void AutoFind::setPort(char *port) {
    this->port = std::stoi(port);
}

bool AutoFind::connect() {
    socket_ = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_ == INVALID_SOCKET){
        log_error("socket create error");
        return false;
    }
    sockaddr_in serverAddress{};
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(static_cast<uint16_t>(port));
    inet_pton(AF_INET, host.c_str(), &(serverAddress.sin_addr));
    // 设置超时时间为5秒
    //+-    int timeout = 5000; // 超时时间（单位：毫秒）
    //+-    setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
    //+-    setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
    if (::connect(socket_, reinterpret_cast<sockaddr *>(&serverAddress),
                  sizeof(serverAddress)) == SOCKET_ERROR) {
        log_error("socket connect error");
        return false;
    }
    return true;
}
///低字节在前
bool AutoFind::sendData(const char *buf, int32_t bufSize) {

    if (!buf || socket_ == INVALID_SOCKET) {
        return false;
    }

    ///// 获取当前时间点
    // 获取当前时间点
    auto now = std::chrono::system_clock::now();

    // 将时间点转换为自1970年1月1日以来的毫秒数
    auto duration = now.time_since_epoch();
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration);

    // 获取64位整数表示
    uint64_t timestamp = milliseconds.count();
    const char * t1 = (const char *)&timestamp;
    if (::send(socket_, t1 , static_cast<int>(8), 0) ==
        SOCKET_ERROR) {
        return false;
    }
    uint32_t va = 0x7A686F75;
    const char * t2 = (const char *)&va;
    if (::send(socket_, t2, static_cast<int>(4), 0) ==
        SOCKET_ERROR) {
        return false;
    }
    const char * t3 = (const char *)&bufSize;
    if (::send(socket_, t3, static_cast<int>(4), 0) ==
        SOCKET_ERROR) {
        return false;
    }
    if (::send(socket_, buf, static_cast<int>(bufSize), 0) ==
        SOCKET_ERROR) {
        return false;
    }
    return true;
}

int32_t AutoFind::recvData(uint8_t *buf, int32_t bufSize) {
    if (!buf || socket_ == INVALID_SOCKET) {
        return 0;
    }

    return recv(socket_, (char *) buf, bufSize, MSG_WAITALL);
}

bool AutoFind::newPacket(SKPacket *packet, int len) {
    packet->data = (uint8_t *)malloc(len * sizeof(uint8_t)); // 分配内存
    if(packet->data == nullptr){
        return false;
    }
    packet->size = len;
    return true;
}

void AutoFind::resetPacket(SKPacket *packet) {
    if(packet->data != nullptr){
        free(packet->data);
    }
    packet->size = 0;
    packet->pts = 0;
    packet->data = nullptr;
}

int AutoFind::receiveLoop() {
    SKPacket *packet = nullptr;
    packet = (struct SKPacket *)malloc(sizeof(struct SKPacket));
    if(packet == nullptr){
        log_error("malloc packet error");
        return -1;
    }
    packet->data = nullptr;
    resetPacket(packet);
    for(;;){

        bool ok = recvPacket(packet);
        if(!ok){
            log_error("recv packet error");
            break;
        }

        ok = pushPacket(packet);
        resetPacket(packet);
        if (!ok) {
            log_error("push packet error");
            break;
        }
    }
    resetPacket(packet);
    free(packet);
    return 0;
}

static AutoFind *autoFind;

///自动发现的udp socket
SOCKET udp_socket = INVALID_SOCKET;

int main()
{
    int ret = 0;
    ///macos,Linux 默认支持
#if defined(_WIN32) || defined(_WIN64)
    //system("chcp 65001");
        // 设置 _O_U8TEXT 模式，使 std::cout 支持 UTF-8
    _setmode(_fileno(stdout), _O_U8TEXT);

    // 设置控制台的输出代码页为 UTF-8
    SetConsoleOutputCP(CP_UTF8);
#endif

    log_info("Auto find Server:%s",PROJECT_VERSION);
    log_info("Build Time:%s",PROJECT_COMPILE_TIME);

    autoFind = new AutoFind();
    log_info("app(%s) start",PROJECT_VERSION);
    udp_socket = ::socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_socket == INVALID_SOCKET)
    {
        log_error("socket error");
        return -1;
    }
    const int opt = 1;
    //设置该套接字为广播类型，
    ret = setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
    if(ret == -1)
    {
        closesocket(udp_socket);
        log_error("set socket error");
        return -1;
    }
    // 设置 recvfrom 的超时时间为 5 秒
    struct timeval timeout;
    timeout.tv_sec = 5;  // 超时时间（秒）
    timeout.tv_usec = 0; // 微秒部分
    if (setsockopt(udp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
        log_error("set socket timeout error");
        closesocket(udp_socket);
        return -1;
    }
    struct sockaddr_in addrto;
    memset(&addrto,0,sizeof(struct sockaddr_in));
    addrto.sin_family=AF_INET;
    addrto.sin_addr.s_addr=htonl(INADDR_BROADCAST);
    addrto.sin_port=htons(AUTO_PORT);
    int nlen=sizeof(addrto);
    char buf[AUTO_SIZE] = {AUTO_FLAG};
    for(int i=0;i<3;i++){
        ret=sendto(udp_socket, buf, static_cast<int>(strlen(buf)), 0, (sockaddr*)&addrto, nlen);
        if(ret<0)
        {
            log_error("send error ret=%d",ret);
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }
        ret= recvfrom(udp_socket, buf, sizeof (buf), 0, (sockaddr*)&addrto, (socklen_t*)&nlen );
        if(ret<=0)
        {
            log_error("read error ret=%d",ret);
            std::this_thread::sleep_for(std::chrono::seconds(1));
            if(i == 2) {
                log_error("can not find server,exit!");
                return ret;
            }
            continue;
        }
        if(std::string(AUTO_FLAG).compare(std::string(buf)) >= 0){
            log_error("read error start:%s",buf);
            continue;
        }
        break;

    }
    char *token;
    char *c = buf;
    ret = 0;
    while ((token = getStrStr(c, "::", &c)) != NULL) {
        ret++;
        if(ret == 2){
            autoFind->setHost(token);
        }else if(ret == 3){
            autoFind->setPort(token);
        }
        //printf("Token: %s\n", token);
    }
    if(!autoFind->connect()){
        log_error("connect error %s",autoFind->getServer().c_str());
        return -1;
    }

    log_info("connect to server %s",autoFind->getServer().c_str());
    ThreadPool::getInstance()->setLog(log_cb);
    ThreadPool::getInstance()->start();
    autoFind->setExit(false);
    std::thread *s_Thread = new std::thread(SendAndRecvThread, autoFind);
    ret = autoFind->receiveLoop();
    autoFind->setExit(true);
    s_Thread->join();
    delete s_Thread;
    log_info("connet closed and exe will exit");
    return ret;
}